"""
########################################
文件描述：用例执行主逻辑，生成测试结果文档
########################################
"""

from common.com_logging import logout
import common.com_request
from common.com_excel import Excel
from common.com_getDict import get_dict
from common.com_db import CommonDB
import xlsxwriter as xw
import time
import sys

# Feature: 标注主要功能模块
# Story: 标注Features功能模块下的分支功能
# Severity: 标注测试用例的重要级别
# Step: 标注测试用例的重要步骤
# Issue和TestCase: 标注Issue、Case，可加入URL
# 用例中的传递参数在接口中命名必须增加用例编号作为前缀，如‘1_userID’，否则无法识别
# 用例中extract列中的参数不可有重复名称


class CaseRun:

    def case_run(self, case_path, sheet):

        # ----------------以下为创建测试结果表格-------------------------------------- #
        report_data = {}
        # 获取当前时间
        now = time.strftime('%Y-%m-%d %H_%M_%S')
        # 拼接测试报告文件名
        file_name = 'testreport_' + now + '.xlsx'
        # 定义报告列标题
        report_title = ['caseID', 'title', 'step', 'type', 'severity', 'Case_Des', 'base', 'url', 'header', 'params',
                        'file', 'response', 'Expected_res', 'result', 'designer']
        # 创建文档
        wb = xw.Workbook(file_name)
        # 增加sheet1,sheet2
        sht_1 = wb.add_worksheet('sheet1')
        sht_2 = wb.add_worksheet('sheet2')
        # 定义全局字体
        wb.formats[0].set_font_name('微软雅黑')
        # 定义标题格式
        title_format = wb.add_format({'bold': True, 'font_color': 'white', 'align': 'center', 'bg_color': '#336666'
                                     , 'font_name': '微软雅黑'})
        # 定义数据写入格式
        data_format = wb.add_format({'font_size': '9', 'font_color': '#272727', 'align': 'left', 'bg_color': '#D1E9E9'
                                     , 'font_name': '微软雅黑', 'border': 1, 'border_color': 'white'})
        # 定义测试结果格式
        pass_format = wb.add_format({'bold': True, 'font_color': 'white', 'align': 'center', 'bg_color': '#01B468'
                                     , 'font_name': '微软雅黑','border': 1, 'border_color': 'white'})
        fail_format = wb.add_format({'bold': True, 'font_color': 'white', 'align': 'center', 'bg_color': '#BF0060'
                                     , 'font_name': '微软雅黑','border': 1, 'border_color': 'white'})
        # 循环获取报告标题，写入sheet
        for report_title_sn in range(len(report_title)):
            sht_1.write(0, report_title_sn, report_title[report_title_sn], title_format)
        # ----------------以下为测试用例执行-------------------------------------- #
        # 获取用例行数
        row_num = Excel('r', case_path).read_row(sheet)
        # 判断用例表是否为空，为空则退出
        if row_num <= 1:
            logout('error', '用例表数据为空')
            # print("=========用例表数据为空===========")
            sys.exit()
        # 接口传递参数字典
        extract_data = {}
        title = list(Excel('r', case_path).row_value(sheet, 0))
        # 定义测试结果写入的行数，从第二行开始（0是第一行）
        row = 1
        # 定义执行第几条用例
        case_no = 1
        # 执行单条用例逻辑
        for row_num_sn in range(row_num - 1):
            # 定义接收测试用例的字典
            data = {}
            # 获取用例表中的数据，row_num_sn控制读取第几行
            case = list(Excel('r', case_path).row_value(sheet, row_num_sn + 1))
            for title_sn in range(len(title)):
                data[title[title_sn]] = case[title_sn]
                title_sn += 1
            # print('=====================用例数据=====================\n', data)
            logout('info', '--------------------------------------------------------------------------------------')
            logout('info', '开始执行第' + str(case_no) + '条用例')
            logout('info', '用例数据为：' + str(data))
            row_num_sn += 1
            params = eval(data['params'], {})
            # 2、判断传递参数的字典（extract_data）中是否有值；
            if extract_data:
                # 将extract_data中的key存入key_list中，方便后面调用
                key_list = list(extract_data.keys()).copy()
                # 3、循环获取key_list中的key，请求正文的参数（value）匹配extract_data中的key；
                for key_list_sn in range(len(key_list)):
                    key_list_key = key_list[key_list_sn]
                    # print('========b:', b)
                    # 当下个接口中需替换的key-value存在相同如【'userID':'userID'】增加'1_'，避免将key也一起替换了
                    # 将params转为str格式，dict格式无法使用replace方法
                    params = str(params)
                    # 判断传递参数是否存在接口请求中
                    if key_list_key in params:
                            # 替换接口请求中的传递参数值
                            new_params = params.replace(key_list_key, extract_data[key_list_key])
                            # 更新请求正文
                            params = new_params
                            logout('info', '替换后的请求正文:' + str(new_params))
                            # print('=========替换后的请求正文==========' '\n', new_params)
                    else:
                        new_params = params
                        logout('info', '=无参数替换=')
                        # print('=无参数替换=' '\n', )
                    key_list_sn += 1
                # 更新用例字典中接口请求的值
                data['params'] = new_params
            # 执行接口并返回接口响应数据，目前支持文本
            res = common.com_request.request(**data).json()
            # 取出响应数据中的所有key，存入data_re字典
            data_re = get_dict().get_dict_allkeys(res)
            logout('info', '响应正文:' + str(res))
            logout('info', '预期结果:' + str(data['Expected_res']))
            # print('============ 响应正文 ============', '\n', res)
            # print('============ 预期结果 ============', '\n', data['Expected_res'], type(data['Expected_res']))
            # 测试结果断言
            # 接收预期结果中每个元素的测试结果
            result_list = []
            # 如果存在预期结果，则进行校验
            if list(data['Expected_res']):
                exp_list = data['Expected_res']
                # 判断预期结果校验类型是否为SQL语句，是的话调用数据库查询方法
                # try:
                if data['Exp_Type'] == 'sql':
                    # 将预期结果列表置空
                    exp_list = []
                    search = CommonDB('../common/config/HXF_Platform_DB.yaml').db_query(str(data['Expected_res']))
                    if search:
                    # 数据库查询结果数据不是标准的列表格式，需要去除多余符号
                        for db_row in range(len(search)):
                            db_re = str(search[db_row]).replace(',', '')
                            db_re = db_re.replace('(', '')
                            db_re = db_re.replace(')', '')
                            db_re = db_re.replace('\'', '')
                            exp_list.append(db_re)
                        logout('info', '数据库查询结果：' + str(exp_list))
                    else:
                        logout('error', '数据库查询结果为空')
                        # 判断当前用例是否存在需提取的值，存在则停止，不存在的话可继续执行后续用例
                        if data['extract']:
                            logout('error', '存在需提取的参数，测试终止')
                            sys.exit()
                        else:
                            logout('warning', '不存在需提取的参数，继续执行下条用例')
                # 预期结果断言，逐项检查
                try:
                    for exp_list_element in exp_list:
                        if str(exp_list_element) in str(res):
                            result_list.append('Pass')
                        else:
                            result_list.append('Fail')
                    if 'Fail' not in result_list:
                        test_result = 'Pass'
                        logout('info', '验证结果：' + str(test_result) + '    预期结果：' + str(exp_list))
                        # print('============ 验证结果 ============', '\n', test_result, '\n', '预期结果：',
                        #       exp_list)
                    else:
                        test_result = 'Fail'
                        logout('info', '验证结果：' + str(test_result) + '    预期结果：' + str(exp_list))
                        # print('============ 验证结果 ============', '\n', test_result, '\n', '预期结果：',
                        #       exp_list)
                    assert test_result == 'Pass'
                except AssertionError as e:
                    # print('=====断言失败=====', e)
                    logout('error', '断言失败：' + str(e))
            else:
                test_result = '预期结果为空，无法判断测试结果'

            # 以下代码为提取当前接口响应中的传递参数并更新extract_data字典
            # 判断用例中【extract】列是否存在数据，如存在则保存至extract_list列表中，给后续接口调用
            if len(data['extract']):
                # split方法将字符串转换为列表
                extract_list = data['extract'].split(",")
                # print(len(extract_list))
                # 循环获取extract_list中的key，并去当前接口的响应数据中匹配value，存入extract_data字典
                num = str(int(data['caseID'])) + '_'
                # print('=====num', num)
                for extract_list_sn in range((len(extract_list))):
                    # print('value of j:', j)
                    extract_list_element = extract_list[extract_list_sn]
                    extract = num + extract_list_element
                    # print('=======b:', b)
                    extract_data[extract] = data_re.get(extract_list_element, 'no value')
            # print('============ 传递参数更新完成 ============', '\n', extract_data)
            logout('info', '传递参数更新完成：' + str(extract_data))

            # ******************************当前接口测试完成、传递参数获取完成******************************#
            # 写入测试结果
            for data_key in data.keys():
                data_value = data[data_key]
                # 当用例字典中的key与测试结果文档中的标题一致时，取出key的值，存入report_data
                if data_key in report_title:
                    report_data[data_key] = data_value
            # report_data字典增加响应数据
            report_data['response'] = str(res)
            report_data['result'] = test_result
            # print('====reportdata===', '\n', report_data, type(report_data))
            # report_data字典中的key与测试报告中的标题进行对比，相同则写入文档
            for Column in range(len(report_title)):  # 列数
                if report_title[Column] in report_data.keys():
                    temp_key = report_title[Column]
                    if temp_key == 'result' and report_data['result'] == 'Pass':
                        sht_1.write(row, Column, report_data.get(temp_key), pass_format)  # 其中的row-行，k-列
                    elif temp_key == 'result' and report_data['result'] == 'Fail':
                        sht_1.write(row, Column, report_data.get(temp_key), fail_format)  # 其中的row-行，k-列
                    else:
                        sht_1.write(row, Column, report_data.get(temp_key), data_format)  # 其中的row-行，k-列
                else:
                    temp_key = report_title[Column]
                    err_data = str(str(temp_key) + ',' + str(report_data.get(temp_key)))
                    sht_2.write(row, 1, err_data)
            logout('info', '第' + str(case_no) + '条用例执行完成，结果为 ' + str(test_result))
            # 写入行数自增
            row += 1
            case_no += 1
        wb.close()
        # 返回测试结果文档名称，供后续生成allure报告调用
        return file_name


if __name__ == '__main__':

    CaseRun().case_run('../testcase/testcase-smoking.xlsx', 'sheet1')

